package org.lds.hotkey.console.business.worker.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.lds.hotkey.common.autoconfigure.etcd.EtcdService;
import org.lds.hotkey.common.constant.KeyConstant;
import org.lds.hotkey.common.model.WorkerModel;
import org.lds.hotkey.common.model.dto.ResultDTO;
import org.lds.hotkey.common.model.pojo.OrderBy;
import org.lds.hotkey.common.model.pojo.PageResult;
import org.lds.hotkey.common.util.AssertUtil;
import org.lds.hotkey.common.util.JsonUtil;
import org.lds.hotkey.console.business.app.service.AppService;
import org.lds.hotkey.console.business.worker.converter.*;
import org.lds.hotkey.console.business.worker.entity.Worker;
import org.lds.hotkey.console.business.worker.mapper.WorkerMapper;
import org.lds.hotkey.console.business.worker.model.request.*;
import org.lds.hotkey.console.business.worker.model.response.*;
import org.lds.hotkey.console.business.worker.service.WorkerBaseService;
import org.lds.hotkey.console.common.enums.ConsoleErrorEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 基础服务实现类
 *
 * @author lidongsheng
 * @since 2024-06-05
 */
@Slf4j
@Service
public class WorkerBaseServiceImpl implements WorkerBaseService {

    @Autowired
    private AppService appService;

    @Autowired
    private WorkerMapper workerMapper;

    @Autowired
    private EtcdService etcdService;

    @Autowired
    private WorkerBaseCreateRequestToEntityConverter workerBaseCreateRequestToEntityConverter;

    @Autowired
    private WorkerBaseUpdateRequestToEntityConverter workerBaseUpdateRequestToEntityConverter;

    @Autowired
    private WorkerBaseListRequestToEntityConverter workerBaseListRequestToEntityConverter;

    @Autowired
    private WorkerToBaseResponseConverter workerToBaseResponseConverter;

    @Autowired
    private WorkerBaseListPageRequestToEntityConverter workerBaseListPageRequestToEntityConverter;

    @Autowired
    private WorkerBaseGetByKeyRequestToEntityConverter workerBaseGetByKeyRequestToEntityConverter;

    @Autowired
    private WorkerBaseDeleteRequestToEntityConverter workerBaseDeleteRequestToEntityConverter;

    @Override
    @Transactional
    public ResultDTO<WorkerBaseResponse> createWorker(WorkerBaseCreateRequest request) {
        if(needCheckApp(request.getAppId())) {
            appService.existApp(request.getAppId());
        }

        try {
            Worker worker = workerBaseCreateRequestToEntityConverter.convert(request);
            workerMapper.insert(worker);

            // 新增成功则同步到ETCD
            boolean success = etcdService.put(KeyConstant.WORKER_DIRECTORY + worker.getAppId() + "/" + worker.getId(),
                    JsonUtil.toJson(WorkerModel.builder().id(worker.getId()).ip(worker.getIp()).port(worker.getPort()).build()));
            AssertUtil.assertTrue(success, ConsoleErrorEnum.ETCD_SYNC_FAIL);

            WorkerBaseResponse response = workerToBaseResponseConverter.convert(worker);
            return ResultDTO.success(response);
        } catch (DuplicateKeyException e) {
            return ResultDTO.fail("DATA_REPEAT", "数据重复");
        }
    }

    private boolean needCheckApp(Long appId) {
        return appId != null && appId != 0L;
    }

    @Transactional
    @Override
    public ResultDTO<WorkerBaseBatchCreateResponse> batchCreateWorker(WorkerBaseBatchCreateRequest request) {
        WorkerBaseBatchCreateResponse response = WorkerBaseBatchCreateResponse.builder()
                .responses(request.getRequests().stream().map(this::createWorker).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Transactional
    @Override
    public ResultDTO<WorkerBaseDeleteResponse> deleteWorker(WorkerBaseDeleteRequest request) {
        WorkerBaseDeleteResponse response = WorkerBaseDeleteResponse.builder().request(request).build();

        Worker worker = workerBaseDeleteRequestToEntityConverter.convert(request);
        List<Worker> workers = workerMapper.selectList(new QueryWrapper<>(worker));
        if (CollectionUtils.isEmpty(workers)) return ResultDTO.success(response);

        workerMapper.delete(new QueryWrapper<>(worker));

        // 删除成功同步ETCD
        for (Worker currWorker : workers) {
            boolean success = etcdService.delete(KeyConstant.WORKER_DIRECTORY + currWorker.getAppId() + "/" + currWorker.getId());
            AssertUtil.assertTrue(success, ConsoleErrorEnum.ETCD_SYNC_FAIL);
        }

        return ResultDTO.success(response);
    }

    @Transactional
    @Override
    public ResultDTO<WorkerBaseBatchDeleteResponse> batchDeleteWorker(WorkerBaseBatchDeleteRequest request) {
        WorkerBaseBatchDeleteResponse response = WorkerBaseBatchDeleteResponse.builder()
                .responses(request.getRequests().stream().map(this::deleteWorker).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Transactional
    @Override
    public ResultDTO<WorkerBaseResponse> updateWorker(WorkerBaseUpdateRequest request) {
        Worker worker = workerBaseUpdateRequestToEntityConverter.convert(request);
        WorkerBaseResponse response = workerToBaseResponseConverter.convert(worker);
        Worker currWorker = workerMapper.selectById(request.getId());
        if (currWorker == null) return ResultDTO.success(response);

        try {
            workerMapper.updateById(worker);

            boolean success = updateETCD(request, currWorker);
            AssertUtil.assertTrue(success, ConsoleErrorEnum.ETCD_SYNC_FAIL);

            return ResultDTO.success(response);
        } catch (DuplicateKeyException e) {
            return ResultDTO.fail("DATA_REPEAT", "数据重复");
        }
    }

    private boolean updateETCD(WorkerBaseUpdateRequest request, Worker currWorker) {
        if (!currWorker.getEnable() && !request.getEnable()) return true;
        if (currWorker.getEnable() && BooleanUtils.isFalse(request.getEnable())) {
            return etcdService.delete(KeyConstant.WORKER_DIRECTORY + currWorker.getAppId() + "/" + currWorker.getId());
        }
        return etcdService.put(KeyConstant.WORKER_DIRECTORY + currWorker.getAppId() + "/" + currWorker.getId(),
                JsonUtil.toJson(WorkerModel.builder()
                        .id(request.getId())
                        .ip(Objects.nonNull(request.getIp()) ? request.getIp() : currWorker.getIp())
                        .port(Objects.nonNull(request.getPort()) ? request.getPort() : currWorker.getPort())
                        .build()));
    }

    @Transactional
    @Override
    public ResultDTO<WorkerBaseBatchUpdateResponse> batchUpdateWorker(WorkerBaseBatchUpdateRequest request) {
        WorkerBaseBatchUpdateResponse response = WorkerBaseBatchUpdateResponse.builder()
                .requests(request.getRequests().stream().map(this::updateWorker).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<WorkerBaseResponse> getWorkerById(WorkerBaseGetByIdRequest request) {
        Worker worker = workerMapper.selectById(request.getId());
        if (worker == null) {
            return ResultDTO.success();
        }

        WorkerBaseResponse response = workerToBaseResponseConverter.convert(worker);
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<WorkerBaseListResponse> getWorkerByIds(WorkerBaseGetByIdsRequest request) {
        List<Worker> list = request.getIds().stream().map(id -> workerMapper.selectById(id)).collect(Collectors.toList());
        return ResultDTO.success(WorkerBaseListResponse.builder()
                .workers(list.stream().map(worker -> workerToBaseResponseConverter.convert(worker)).collect(Collectors.toList()))
                .build());
    }

    @Override
    public Worker getWorkerByKey(WorkerBaseGetByKeyRequest request) {
        return workerMapper.selectOne(new QueryWrapper<>(workerBaseGetByKeyRequestToEntityConverter.convert(request)));
    }

    @Override
    public ResultDTO<WorkerBaseListResponse> listWorker(WorkerBaseListRequest request) {
        QueryWrapper<Worker> wrapper = new QueryWrapper<>(workerBaseListRequestToEntityConverter.convert(request));
        if (StringUtils.isNotEmpty(request.getOrderByKey())) {
            wrapper.orderByDesc(request.getOrderByKey());
        }

        List<Worker> workers = workerMapper.selectList(wrapper);

        WorkerBaseListResponse response = WorkerBaseListResponse.builder()
                .workers(workers.stream().map(workerToBaseResponseConverter::convert).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<WorkerBaseListResponse> listAllWorker(OrderBy orderBy) {
        WorkerBaseListRequest workerRequest = WorkerBaseListRequest.builder().build();
        workerRequest.setOrderByKey(orderBy.getOrderByKey());
        return listWorker(workerRequest);
    }

    @Override
    public ResultDTO<PageResult<WorkerBaseResponse>> listWorkerPage(WorkerBaseListPageRequest request) {
        Page<Worker> page = new Page<>(request.getPageNum(), request.getPageSize(), true);

        QueryWrapper<Worker> wrapper = new QueryWrapper<>(workerBaseListPageRequestToEntityConverter.convert(request));
        if (Objects.nonNull(request.getGtId())) {
            wrapper.gt("id", request.getGtId());
        }

        if (StringUtils.isNotEmpty(request.getOrderByKey())) {
            if (request.getOrderByDesc() != null && request.getOrderByDesc().booleanValue() == false) {
                wrapper.orderByAsc(request.getOrderByKey());
            } else {
                wrapper.orderByDesc(request.getOrderByKey());
            }
        }

        IPage<Worker> pageResult = workerMapper.selectPage(page, wrapper);

        PageResult<WorkerBaseResponse> response = new PageResult(
                pageResult.getTotal(), pageResult.getPages(), pageResult.getCurrent(), pageResult.getSize(),
                pageResult.getRecords().stream().map(workerToBaseResponseConverter::convert).collect(Collectors.toList()));
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<WorkerBaseBatchCreateResponse> batchCreateWorkerAsync(WorkerBaseBatchCreateRequest request) {
        WorkerBaseBatchCreateResponse response = WorkerBaseBatchCreateResponse.builder()
                .responses(request.getRequests().parallelStream().map(this::createWorker).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<WorkerBaseBatchDeleteResponse> batchDeleteWorkerAsync(WorkerBaseBatchDeleteRequest request) {
        WorkerBaseBatchDeleteResponse response = WorkerBaseBatchDeleteResponse.builder()
                .responses(request.getRequests().parallelStream().map(this::deleteWorker).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<WorkerBaseBatchUpdateResponse> batchUpdateWorkerAsync(WorkerBaseBatchUpdateRequest request) {
        WorkerBaseBatchUpdateResponse response = WorkerBaseBatchUpdateResponse.builder()
                .requests(request.getRequests().parallelStream().map(this::updateWorker).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

}
